package com.shuqi.download.core;

import java.io.File;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import com.shuqi.download.database.DownloadInfo;
import com.shuqi.download.database.DownloadInfoDao;
import com.shuqi.download.intf.DownloadManagerInterface;
import com.shuqi.download.listener.DownloadStateListener;
import com.shuqi.download.util.DownloadStatus;
import com.shuqi.download.util.PausableThreadPoolExecutor;
import com.shuqi.download.util.Util;
import com.shuqi.download.util.Constant;

/**
 * 下载管理器
 * @author GaoPeng<gaopeng@ucweb.com>
 *
 */
public class DownloadManager implements DownloadManagerInterface {
	
	private static DownloadManager instance=null;
	
	// 下载监听器
	private List<DownloadStateListener> listeners;
	// 线程池
	private ThreadPoolExecutor pool;
	// 映射表
	private Hashtable<String, FileThread> fileMap;
	
    /**
     * 
     * 注册下载状态监听
     * 
     * @param listener
     */
    public void registerDownStateListener(DownloadStateListener listener) {
        if (!listeners.contains(listener)) {
        	listeners.add(listener);
        	Util.log("监听器注册成功（"+listener+"）");
        }
    }
    
    /**
     * 
     * 取消注册状态监听
     * 
     * @param listener
     */
    public void unRegisterDownStateListener(DownloadStateListener listener) {
        if (listeners.contains(listener)) {
        	listeners.remove(listener);
        	Util.log("监听器删除成功（"+listener+"）");
        }
    }
    
    /**
     * 从映射表中删除文件线程
     * @param key
     */
    public void deleteFileMap(String key) {
    	if(fileMap!=null&&fileMap.containsKey(key)) {
    		fileMap.remove(key);
    	}
    }
    
    /**
     * 通知下载状态
     * @param uid
     * @param bid
     * @param state
     * @param percent
     */
    protected void notifyDownloadState(String uid, String bid, int state, float percent) {
    	Util.log("回传下载状态（uid:"+uid+",bid:"+bid+",state:"+state+",percent:"+percent+"）");
        for (DownloadStateListener listener:listeners) {
        	listener.updateDownState(uid,bid,state,percent);
        }
    }
    
	private DownloadManager() {
		/**
		 * ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long
		 * keepAliveTime, TimeUnit unit, BlockingQueue<Runnable>
		 * workQueue,RejectedExecutionHandler handler) corePoolSize：
		 * 池中所保存的线程数，包括空闲线程。 maximumPoolSize：池中允许的最大线程数。 keepAliveTime：
		 * 当线程数大于核心时，此为终止前多余的空闲线程等待新任务的最长时间。 unit：keepAliveTime 参数的时间单位。
		 * workQueue： 执行前用于保持任务的队列。此队列仅由保持 execute 方法提交的 Runnable 任务。
		 * handler： 由于超出线程范围和队列容量而使执行被阻塞时所使用的处理程序。
		 */
		pool = new PausableThreadPoolExecutor(Constant.DOWNLOAD_POOL_SIZE, Constant.DOWNLOAD_POOL_SIZE, 3,
				TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(
						Constant.DOWNLOAD_POOL_SIZE), new ThreadPoolExecutor.DiscardPolicy());
		fileMap = new Hashtable<String, FileThread>();
		listeners = new ArrayList<DownloadStateListener>();
    }
	
    public static synchronized DownloadManager getInstance() {
        if(instance==null) {
        	instance=new DownloadManager();
        }
        return instance;
    }
    
    /**
     * 初始化下载任务数据表
     * @param uid
     * @param bid
     * @return
     */
    private boolean initDownloadInfo(String uid,String bid) {
        DownloadInfo downloadInfo=DownloadInfoDao.getInstance().getDownloadInfo(uid, bid);
        if(!Util.isEmpty(uid)&&!Util.isEmpty(bid)&&downloadInfo==null) {
            downloadInfo=new DownloadInfo();
            downloadInfo.setUserId(uid);
            downloadInfo.setBookId(bid);
            downloadInfo.setRecordType(1);
            DownloadInfoDao.getInstance().save(downloadInfo);
        }
        if(downloadInfo==null) {
            return false;
        } else {
            return true;
        }
    }
    
    /**
     * 添加下载任务
     * @param uid   用户ID
     * @param bid   书籍ID
     */
    @Override
    public synchronized void add(String uid,String bid) {
        if(fileMap!=null&&!fileMap.containsKey(uid+"_"+bid)) {
            Util.log("接受添加下载命令（uid:"+uid+",bid:"+bid+"）");
            if(initDownloadInfo(uid,bid)) {
                FileThread fileThread=new FileThread(uid,bid);
                fileMap.put(uid+"_"+bid, fileThread);
                // 设置下载状态为等待下载（必须放入fileMap中后再执行通知，否则get到的DownloadInfo可能为空）
                notifyDownloadState(uid,bid,DownloadStatus.STATE_WAIT,-1);
            } else {
                Util.log("下载任务创建失败（uid:"+uid+",bid:"+bid+"）");
            }
        } else {
            Util.log("下载任务已存在（uid:"+uid+",bid:"+bid+"）");
        }
    }
    
    /**
     * 移除下载任务
     * @param uid   用户ID
     * @param bid   书籍ID
     */
    @Override
    public synchronized void remove(String uid,String bid) {
        Util.log("接受移除下载命令（uid:"+uid+",bid:"+bid+"）");
        if(fileMap!=null&&fileMap.containsKey(uid+"_"+bid)) {
            fileMap.remove(uid+"_"+bid);
        }
    }
    
    /**
     * 执行下载任务
     * @param uid   用户ID
     * @param bid   书籍ID
     * @param url   下载地址
     * @param poolSize  单文件下载线程数
     * @param progressType  进度回传类型（默认为0，按百分比回传；1为按时间间隔回传）
     * @param progressUnit  回传单位（n/%或n毫秒）
     */
    @Override
    public synchronized void exec(String uid,String bid,String url,int poolSize,int progressType,int progressUnit) {
        if(fileMap!=null) {
            if(fileMap.containsKey(uid+"_"+bid)) {
                Util.log("接受执行下载命令（uid:"+uid+",bid:"+bid+",url:"+url+",poolSize:"+poolSize+",progressType:"+progressType+",progressUnit:"+progressUnit+"）");
                FileThread fileThread=fileMap.get(uid+"_"+bid);
                if(fileThread!=null) {
                    fileThread.init(url, poolSize, progressType, progressUnit);
                    fileMap.put(uid+"_"+bid, fileThread);
                    pool.execute(fileThread);
                }
            } else {
                Util.log("下载任务未添加（uid:"+uid+",bid:"+bid+"）");
            }
        }
    }
    
    /**
     * 执行下载任务
     * @param uid   用户ID
     * @param bid   书籍ID
     * @param url   下载地址
     * @param poolSize  单文件下载线程数
     */
    @Override
    public synchronized void exec(String uid,String bid,String url,int poolSize) {
        exec(uid,bid,url,poolSize,0,0);
    }
    
    /**
	 * 开始下载任务
	 * @param uid	用户ID
	 * @param bid	书籍ID
	 * @param url	下载地址
	 * @param poolSize	单文件下载线程数
	 * @param progressType	进度回传类型（默认为0，按百分比回传；1为按时间间隔回传）
	 * @param progressUnit	回传单位（n/%或n毫秒）
	 */
    @Override
	public synchronized void start(String uid,String bid,String url,int poolSize,int progressType,int progressUnit) {
    	if(fileMap!=null) {
    	    if(!fileMap.containsKey(uid+"_"+bid)) {
    	        Util.log("接受开始下载命令（uid:"+uid+",bid:"+bid+",url:"+url+",poolSize:"+poolSize+",progressType:"+progressType+",progressUnit:"+progressUnit+"）");
    	        if(initDownloadInfo(uid,bid)) {
    	            FileThread fileThread=new FileThread(uid,bid);
                    fileMap.put(uid+"_"+bid, fileThread);
                    // 设置下载状态为等待下载（必须放入fileMap中后再执行通知，否则get到的DownloadInfo可能为空）
                    notifyDownloadState(uid,bid,DownloadStatus.STATE_WAIT,-1);
                    // init会影响到notify的速度，所以放在后面
                    fileThread.init(url, poolSize, progressType, progressUnit);
                    fileMap.put(uid+"_"+bid, fileThread);
                    pool.execute(fileThread);
                    //pool.shutdown();
    	        } else {
                    Util.log("下载任务创建失败（uid:"+uid+",bid:"+bid+"）");
                }
    	    } else {
    	        Util.log("重复发送开始下载命令（任务已经存在）");
    	    }
    	}
	}
    
    /**
     * 开始下载任务
     * @param uid   用户ID
     * @param bid   书籍ID
     * @param url   下载地址
     * @param poolSize  单文件下载线程数
     */
    @Override
	public void start(String uid,String bid,String url,int poolSize) {
		start(uid,bid,url,poolSize,0,0);
	}

    /**
     * 开始所有下载任务
     */
	@Override
	public void startAll() {
		// 待实现（注意线程安全）
	}

	/**
     * 停止下载任务
     * @param uid   用户ID
     * @param bid   书籍ID
     */
	@Override
	public void stop(String uid,String bid) {
		Util.log("接受停止下载命令（uid:"+uid+",bid:"+bid+"）");
		if(fileMap!=null&&fileMap.containsKey(uid+"_"+bid)) {
			fileMap.get(uid+"_"+bid).stop();
			fileMap.remove(uid+"_"+bid);
		}
	}
	
	/**
	 * 停止所有下载任务
	 */
	@Override
	public void stopAll() {
	    /*
		if(fileMap!=null) {
			Enumeration<FileThread> em=fileMap.elements();
			while(em.hasMoreElements()) {
				// 待实现（注意线程安全）
			}
		}*/
	}
	
	/**
	 * 删除下载任务
     * @param uid   用户ID
     * @param bid   书籍ID
	 */
	@Override
	public void delete(String uid, String bid) {
		Util.log("接受删除下载命令（uid:"+uid+",bid:"+bid+"）");
		// 先停止下载任务
		stop(uid,bid);
		// 删除数据库记录
		DownloadInfoDao.getInstance().deleteDownloadInfo(uid, bid);
		// 删除下载文件
        File file=new File(Constant.CACHE_PATH+"/"+Util.parseFileName(uid, bid));
		if(file!=null&&file.exists()) {
			file.delete();
		}
	}
	
	/**
	 * 删除所有下载任务
	 */
	@Override
	public void deleteAll() {
		// 待实现（注意线程安全）
	}

	/**
	 * 获取下载任务
     * @param uid   用户ID
     * @param bid   书籍ID
	 */
	@Override
	public synchronized DownloadInfo get(String uid, String bid) {
		Util.log("接受获取下载命令（uid:"+uid+",bid:"+bid+"）");
		DownloadInfo downloadInfo=null;
		if(fileMap!=null) {
		    FileThread fileThread=fileMap.get(uid+"_"+bid);
	        if(fileThread!=null) {
	            downloadInfo=fileThread.getDownloadInfo();
	        }
		}
		if(downloadInfo==null) {
		    downloadInfo=DownloadInfoDao.getInstance().getDownloadInfo(uid, bid);
		} else {
		    Util.log("从线程中获取到下载信息");
		}
		return downloadInfo;
	}

	/**
	 * 获取所有下载任务
	 */
	@Override
	public List<DownloadInfo> getAll() {
		Util.log("接受获取所有下载命令");
		return DownloadInfoDao.getInstance().getAllDownloadInfo();
	}
	
	/**
	 * 清理当前实体（否则程序退出可能还alive）
	 */
	public void clear() {
        DownloadInfoDao.getInstance().clear();
	    instance=null;
	}
}
